﻿using DocumentFormat.OpenXml.Lite.Core.Interfaces;
using DocumentFormat.OpenXml.Lite.Core.Shared;
using DocumentFormat.OpenXml.Lite.Xml;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Schema;
using System.Xml.Serialization;
using IO = System.IO;

namespace DocumentFormat.OpenXml.Lite.Core.Office
{
    public class CoreProperties : XmlSerializable, ISerializable
    {
        const string CoreProps = "http://schemas.openxmlformats.org/package/2006/metadata/core-properties";
        const string Dc = "http://purl.org/dc/elements/1.1/";
        const string DcTerms = "http://purl.org/dc/terms/";
        const string DcmiType = "http://purl.org/dc/dcmitype/";
        const string Xsi = "http://www.w3.org/2001/XMLSchema-instance";

        #region Elements

        [Optional]
        [XmlElement("category", Namespace = CoreProps)]
        public SimpleElement<string> Category { get; set; }

        [Optional]
        [XmlElement("contentStatus", Namespace = CoreProps)]
        public SimpleElement<string> ContentStatus { get; set; }

        [Optional]
        [XmlElement("created", Namespace = DcTerms)]
        public XsiItem<DateTime> Created { get; set; }

        [Optional]
        [XmlElement("creator", Namespace = Dc)]
        public SimpleElement<string> Creator { get; set; }

        [Optional]
        [XmlElement("description", Namespace = Dc)]
        public SimpleElement<string> Description { get; set; }

        [Optional]
        [XmlElement("identifier", Namespace = Dc)]
        public SimpleElement<string> Identifier { get; set; }

        [Optional]
        [XmlElement("keywords", Namespace = CoreProps)]
        public SimpleElement<string> Keywords { get; set; }

        [Optional]
        [XmlElement("language", Namespace = Dc)]
        public SimpleElement<string> Language { get; set; }

        [Optional]
        [XmlElement("lastModifiedBy", Namespace = CoreProps)]
        public SimpleElement<string> LastModifiedBy { get; set; }

        [Optional]
        [XmlElement("lastPrinted", Namespace = CoreProps)]
        public SimpleElement<DateTime> LastPrinted { get; set; }

        [Optional]
        [XmlElement("modified", Namespace = DcTerms)]
        public XsiItem<DateTime> Modified { get; set; }

        [Optional]
        [XmlElement("revision", Namespace = CoreProps)]
        public SimpleElement<string> Revision { get; set; }

        [Optional]
        [XmlElement("subject", Namespace = Dc)]
        public SimpleElement<string> Subject { get; set; }

        [Optional]
        [XmlElement("title", Namespace = Dc)]
        public SimpleElement<string> Title { get; set; }

        [Optional]
        [XmlElement("version", Namespace = CoreProps)]
        public SimpleElement<string> Version { get; set; }

        #endregion

        [XmlNamespaceDeclarations]
        public XmlSerializerNamespaces Namespaces { get; set; }

        public CoreProperties()
        {
            Namespaces = new XmlSerializerNamespaces();
            Namespaces.Add("cp", CoreProps);
            Namespaces.Add("dc", Dc);
            Namespaces.Add("dcterms", DcTerms);
            Namespaces.Add("dcmitype", DcmiType);
            Namespaces.Add("xsi", Xsi);
        }

        #region Public Methods

        public byte[] Serialize()
        {
            XmlSerializer serializer = new XmlSerializer(this.GetType());
            using (var stream = new IO.MemoryStream())
            {
                using (var writer = new IO.StreamWriter(stream, Encoding.UTF8))
                {
                    using (var xwriter = XmlWriter.Create(writer))
                    {
                        xwriter.WriteStartElement("cp", "coreProperties", CoreProps);
                        foreach (var ns in Namespaces.ToArray())
                        {
                            if (ns.Name != "")
                            {
                                xwriter.WriteAttributeString("xmlns", ns.Name, null, ns.Namespace);
                            }
                        }
                        this.WriteXml(xwriter);
                    }
                    return stream.ToArray();
                }
            }
        }

        public void Deserialize(System.IO.Stream content)
        {
            using (var reader = XmlReader.Create(content))
            {
                reader.ReadToDescendant("coreProperties", CoreProps);
                this.ReadXml(reader);
            }
        }

        #endregion
    }

    public class XsiItem<T> : SimpleElement<T>
    {
        [Optional]
        [XmlAttribute("type", Namespace = "http://www.w3.org/2001/XMLSchema-instance", Form = XmlSchemaForm.Qualified)]
        public string Type { get; set; }

        public XsiItem() { }

        public XsiItem(T value, string type)
            : base(value)
        {
            this.Type = type;
        }
    }
}
